#include <iostream>
#include <string.h>
#include <memory>
#include <string>

using std::cout;
using std::endl;
using std::string;
// 了解std::allocator的用法之后,实现自定义的Vector类

// 接口形式：
class Computer
{
public:
    Computer(const char* name, double price)
        : _name( new char[strlen(name) + 1] ())
        , _price(price){
            strcpy(_name, name);
        }
    Computer(const Computer & pc)
        : _name(new char[strlen(pc._name) + 1] ())
        , _price(pc._price){
            strcpy(_name, pc._name);
        }
    ~Computer(){
        if(_name != nullptr)
        {
            delete [] _name;
            _name = nullptr;
        }
    }
    Computer& operator=(const Computer& pc){
        _name = new char[strlen(pc._name) + 1] ();
        strcpy(_name, pc._name);
        _price = pc._price;
        return *this;
    }

    void print(){cout << _name << ", " << _price << endl;}
private:
    char* _name;
    double _price;
};

template <class T>
class Vector
{
public:
    Vector() {}
    ~Vector() { while(_start != _finish){_alloc.destroy(--_finish);} _alloc.deallocate(_start, capacity()); }
    void push_back(const T &value);
    void pop_back();
    size_t size() const { return _finish - _start; }
    size_t capacity() const { return _end_of_storage - _start; }
    bool empty() const { return _start == _finish; }
    T *begin() const { return _start; }
    T *end() const { return _finish; }

private:
    void reallocate(); // 重新分配内存,动态扩容要用的

private:
    static std::allocator<T> _alloc;
    T *_start = nullptr;          // 指向数组中的第一个元素
    T *_finish = nullptr;         // 指向最后一个实际元素之后的那个元素
    T *_end_of_storage = nullptr; // 指向数组本身之后的位置
};
template <class T>
std::allocator<T> Vector<T>::_alloc;


template <class T>
void Vector<T>::push_back(const T& value){
    if(size() < capacity()){
        _alloc.construct(_finish++, value);
    }else{
        // 重新分配空间
        reallocate();
        push_back(value);
    }
}

template <class T>
void Vector<T>::pop_back(){
    if(size() == 0){
        throw "size() == 0";
    }else{
        _alloc.destroy(--_finish);
    }
}

template <class T>
void Vector<T>::reallocate(){
    size_t s = size();
    T* t_start = _start;

    if(s == 0){
        _start = _alloc.allocate(1);
        _finish = _start;
        _end_of_storage = _start + 1;
    }else{
        _start = _alloc.allocate(s << 1);
        memcpy(_start, t_start, s * sizeof(T));
        _finish = _start + s;
        _end_of_storage = _start + (s << 1);
        _alloc.deallocate(t_start, s);
    }
}


int main()
{
    Vector<Computer> v;
    v.push_back({"huzhu", 1000});
    
    cout << v.size() << endl;
    cout << v.capacity() << endl << endl;

    v.push_back({"wangdao", 1000});
    cout << v.size() << endl;
    cout << v.capacity() << endl << endl;

    v.push_back({"jinhong", 1000000000});
    cout << v.size() << endl;
    cout << v.capacity() << endl << endl;




    for(auto& elem : v){
        elem.print();
    }
    cout << endl << endl;


    v.pop_back();

    
    cout << v.size() << endl;
    cout << v.capacity() << endl << endl;
    for(auto& elem : v){
        elem.print();
    }
    cout << endl;
    return 0;
}




template <class _Tp>
class allocator
{
    typedef alloc _Alloc; // The underlying allocator.
public:
    typedef size_t size_type;
    typedef ptrdiff_t difference_type;
    typedef _Tp *pointer;
    typedef const _Tp *const_pointer;
    typedef _Tp &reference;
    typedef const _Tp &const_reference;
    typedef _Tp value_type;

    template <class _Tp1>
    struct rebind
    {
        typedef allocator<_Tp1> other;
    };

    allocator() __STL_NOTHROW {}
    allocator(const allocator &) __STL_NOTHROW {}
    template <class _Tp1>
    allocator(const allocator<_Tp1> &) __STL_NOTHROW {}
    ~allocator() __STL_NOTHROW {}

    pointer address(reference __x) const { return &__x; }
    const_pointer address(const_reference __x) const { return &__x; }

    // __n is permitted to be 0.  The C++ standard says nothing about what
    // the return value is when __n == 0.
    _Tp *allocate(size_type __n, const void * = 0)
    {
        return __n != 0 ? static_cast<_Tp *>(_Alloc::allocate(__n * sizeof(_Tp))) : 0;
    }

    // __p is not permitted to be a null pointer.
    void deallocate(pointer __p, size_type __n)
    {
        _Alloc::deallocate(__p, __n * sizeof(_Tp));
    }

    size_type max_size() const __STL_NOTHROW
    {
        return size_t(-1) / sizeof(_Tp);
    }

    void construct(pointer __p, const _Tp &__val) { new (__p) _Tp(__val); }
    void destroy(pointer __p) { __p->~_Tp(); }
};